package com.saikei.demoapp.util

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout


/**
 *
 * @author saikei
 * @date 2025/7/1 18:18
 */


/**
 *
 * @author saikei
 * @date 2025/7/1 18:18
 */
class Coroutine<T>(
    private val scope: CoroutineScope,
    private val onLoad: suspend CoroutineScope.() -> T
) {
    private var onStart: (suspend CoroutineScope.() -> Unit)? = null
    private var onSuccess: (suspend CoroutineScope.(T) -> Unit)? = null
    private var onError: (suspend CoroutineScope.(Throwable) -> Unit)? = null
    private var onFinally: (suspend CoroutineScope.() -> Unit)? = null
    private var timeout: Long = 10000


    fun onStart(block: suspend CoroutineScope.() -> Unit): Coroutine<T> {
        this.onStart = block
        return this
    }

    fun onSuccess(block: suspend CoroutineScope.(T) -> Unit): Coroutine<T> {
        this.onSuccess = block
        return this
    }

    fun onError(block: suspend CoroutineScope.(Throwable) -> Unit): Coroutine<T> {
        this.onError = block
        return this
    }

    fun onFinally(block: suspend CoroutineScope.() -> Unit): Coroutine<T> {
        this.onFinally = block
        return this
    }

    fun timeout(millis: Long): Coroutine<T> {
        this.timeout = millis
        return this
    }

    /**
     * 如果你用 lifecycleScope 或 viewModelScope
     * 系统会帮你自动取消，不用手动调用。
     * 如果你自己创建了一个 CoroutineScope
     */
    fun cancelJob(){
        kotlin.runCatching {
            scope.cancel()
        }
    }

    fun async(): Coroutine<T> {
        scope.launch(Dispatchers.Main) {
            onStart?.invoke(this)
            val result = runCatching {
                withContext(Dispatchers.IO) {
                    if (timeout > 0) withTimeout(timeout) { onLoad() }
                    else onLoad()
                }
            }
            result.onSuccess {
                onSuccess?.invoke(this, it)
            }.onFailure {
                onError?.invoke(this, it)
            }
            onFinally?.invoke(this)
        }
        return this
    }
}
